#include <GL/glut.h>
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "helpers.h"
#include "shaderutils.h"




CvHaarClassifierCascade *cascade_f;
CvHaarClassifierCascade *cascade_e;
CvMemStorage			*storage;




GLuint shaderFilter = 0;
GLuint shaderImmortal = 0;
GLuint shaderEdge = 0;
GLuint filterTextureLocation = 0;
GLuint filterTextureLocation2 = 0;
GLuint immortalTextureLocation = 0;
GLuint immortalTextureLocation2 = 0;
GLuint edgeTextureLocation = 0;
GLuint edgeTextureLocation2 = 0;
GLuint stateLocation = 0;
GLuint pic, pic2, fbotex,  anothertex;
int hell = 0;
int ind = 0;
float ind2 = 0.0;
int ball = 0;
int drawingBall = 0;
GLfloat state = 0.0;

CvCapture* g_Capture;

float ballPos = 1.0;

void drawBall() {
  glPushMatrix();
  glTranslatef(-1.0+(0.1*ballPos),-0.5,0.0);
  glutSolidSphere(0.1, 15, 15);
  
  if (ballPos < 20.0) {  //Behövs lite till, är 20 för att kunna debugga
  ballPos += 0.1;
  }
  else {
    ball = 0;
    drawingBall = 0;
    ballPos = 1.0;
  }
  glPopMatrix();


}

void HandleKeyboard(unsigned char key,int x, int y)
{
  switch (key) {
  case 'a':
    if (hell == 2) {
      hell = 0;
    }
    else {
      hell += 1;
    }
    break;
  case 'b':
    ball = 1 - ball;
    break;
  case 27:
    //glutLeaveGameMode(); //set the resolution how it was
    exit(0); //quit the program
    break;
  }
glutPostRedisplay();

}

GLuint fb, rb;
GLfloat light_position[] = { 0.0, 0.0, 0.0, 1.0 };

void initFBO() {
  
  // initialize texture 1
  glGenTextures(1, fbotex);
  glBindTexture(GL_TEXTURE_2D, fbotex);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, 
	       512, 512, 0, GL_RGBA, GL_FLOAT, 0L); // NULL = Empty texture
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // FBO safe
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  
  glGenFramebuffers(1, &fb);
  glGenRenderbuffersEXT(1, &rb);
  glBindFramebufferEXT(GL_FRAMEBUFFER, fb);
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbotex, 0);
  glBindRenderbufferEXT(GL_RENDERBUFFER, rb);
  glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512);
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rb);

  

}

CvSize size;
CvVideoWriter *videoWriter;

void init()
{

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  //initFBO();

  // Set default material properties
  GLfloat mat_shininess[] = { 50.0 };
  GLfloat mat_diffuseColor[] = { 1.0, 1.0, 1.0, 1.0 };
  GLfloat mat_specularColor[] = { 1.0, 1.0, 1.0, 1.0 };

  glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
  glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuseColor);
  glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specularColor);
  GLfloat ambientColor [] = { 1.0, 1.0, 1.0, 0.9 };
  GLfloat diffuseColor[] = { 1.0, 1.0, 1.0, 1.0 };
  GLfloat specularColor[] = { 1.0, 1.0, 1.0, 0.9 };
  glLightfv(GL_LIGHT0, GL_AMBIENT, ambientColor);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseColor);
  glLightfv(GL_LIGHT0, GL_SPECULAR, specularColor);
  glMatrixMode(GL_MODELVIEW);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_NORMALIZE);
 
  glEnable(GL_DEPTH_TEST);

  //pic = loadTexture("textures/test.jpg");
  //pic = loadTexture("textures/green_test.jpg");
  pic2 = loadTexture("textures/weather.jpg");

  // Create vertex shader
  GLuint vertexShader = createShaderFromFile(GL_VERTEX_SHADER, "vertexShader.vs");
  // Create fragment shader
  GLuint fragmentShader = createShaderFromFile(GL_FRAGMENT_SHADER, "filter.fs");
  // Create shader program
  shaderFilter = createShaderProgram(vertexShader, fragmentShader);
  fragmentShader = createShaderFromFile(GL_FRAGMENT_SHADER, "immortal.fs");
  shaderImmortal = createShaderProgram(vertexShader, fragmentShader);
  fragmentShader = createShaderFromFile(GL_FRAGMENT_SHADER, "edge.fs");
  shaderEdge = createShaderProgram(vertexShader, fragmentShader);
  filterTextureLocation = getUniformLocation(shaderFilter, "texture");
  filterTextureLocation2 = getUniformLocation(shaderFilter, "texture2");
  immortalTextureLocation = getUniformLocation(shaderImmortal, "texture");
  immortalTextureLocation2 = getUniformLocation(shaderImmortal, "texture2");
  edgeTextureLocation = getUniformLocation(shaderImmortal, "texture");
  edgeTextureLocation2 = getUniformLocation(shaderImmortal, "texture2");
  stateLocation = getUniformLocation(shaderFilter, "state");
}

void display()
{ 
  glClearColor(1.0, 1.0, 1.0, 1);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);
  
  glEnable(GL_TEXTURE_2D);

  if (ball == 1 || drawingBall == 1) {
    drawBall();
  }
  
   //else {
    //------------------------------------------------
  //Webcam code
  //------------------------------------------------
  
  // Capture next frame
  IplImage *image = cvQueryFrame(g_Capture);    
    
  // Convert to RGB
  cvCvtColor(image, image, CV_BGR2RGB);
  
  
  // Create Texture
  glBindTexture(GL_TEXTURE_2D, pic);// NULL = Empty texture
 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, image->width, image->height, GL_RGB, GL_UNSIGNED_BYTE, image->imageData);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // FBO safe
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 
  
  //------------------------------------------------
  //
  //------------------------------------------------

  //glActiveTexture(GL_TEXTURE0);
  if (hell == 0) {
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, pic2);
    glUseProgram(shaderFilter);
    glUniform1i(filterTextureLocation2, 1);
    glUniform1f(stateLocation, state);
    glActiveTexture(GL_TEXTURE0);
    glUniform1i(filterTextureLocation, 0);
    }
  else if (hell == 1) {
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, pic2);
    glUseProgram(shaderImmortal);
    glUniform1i(filterTextureLocation2, 1);
    glUniform1f(stateLocation, state);
    glActiveTexture(GL_TEXTURE0);
    glUniform1i(filterTextureLocation, 0);
    }
  else if (hell == 2) {
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, pic2);
    glUseProgram(shaderEdge);
    glUniform1i(edgeTextureLocation2, 1);
    glUniform1f(stateLocation, state);
    glActiveTexture(GL_TEXTURE0);
    glUniform1i(edgeTextureLocation, 0);
    }



  
  //glBindTexture(GL_TEXTURE_2D, pic);
  glBegin(GL_QUADS);
  glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, -1.0, 0.0);
  glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, 1.0, 0.0);
  glTexCoord2f(1.0, 0.0); glVertex3f(1.0, 1.0, 0.0);
  glTexCoord2f(1.0, 1.0); glVertex3f(1.0, -1.0, 0.0);
  glEnd();
  if (hell == 1) {
    glUseProgram(0);
  }
  glDisable(GL_TEXTURE_2D);
  //}
  glFrustum(-0.1, 0.1, -0.1, 0.1, 0.1, 700);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  
  // Swap front- and backbuffers

  glutSwapBuffers();
}

float hold1 = 0.0;
int hold2 = 0;



void idle()
{

  /* 
  */




  // As soon as the machine is idle, ask GLUT to trigger rendering of a new
  // frame
  glutPostRedisplay();
}
void reshape(GLsizei w, GLsizei h)
{	
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-0.5, 0.5, -0.5, 0.5, 0.5, 20); // left, right, bottom, top, near, far
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0, 0, 2.5, 0, 0, 0, 0, 1, 0); // camera, look-at-point, up-vector
}

int main(int argc, char **argv)
{
  glutInit(&argc, argv);

  glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
  glutInitWindowSize(800, 800);
  glutCreateWindow("BMC-FX");
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(90, 1, 0.01, 1000);
  glMatrixMode(GL_MODELVIEW);

  //--------------------------------------------
  //Initialize webcam
  //--------------------------------------------
  
  g_Capture = cvCreateCameraCapture(0);
  assert(g_Capture);
  
  //--------------------------------------------
  //
  //--------------------------------------------

  //--------------------------------------------
  //OpenCV shit
  //--------------------------------------------


  //--------------------------------------------
  //
  //--------------------------------------------

  initHelperLibrary();
  init();
  glutReshapeFunc(reshape);
  glutDisplayFunc(display);
  glutIdleFunc(idle);
  glutKeyboardFunc(HandleKeyboard);

  //glutGameModeString( "1024x768:32@75" ); //the settings for fullscreen mode
  //glutEnterGameMode(); 

  glutMainLoop();

  return 0;
}

